13 协议

13.1 正式协议

说明:正式协议类似Java的接口。

  • 声明协议:通过@protocol创建协议的声明。
  • 采用协议:在类的@interface声明中列出协议的名称。
  • 遵守协议:在类的@implementation中实现协议的所有方法(否则编译器会生成警告)。

13.1.1 声明协议

说明:类似类或类别的声明

  • 可以继承父协议,类似继承父类
  • 内部是方法声明列表
  • 在协议中不会引入新的实例变量

语法:

1
2
3
@protocol 协议名 <父协议名
// 方法列表
@end

Cocoa的NSCopying协议

说明:如果采用了该协议,对象就会知道如何创建自己的副本。

1
2
3
@protocol NSCopying
- (id) copyWithZone: (NSZone *) zone;
@end

Cocoa的NSCoding协议

说明:能够对自身进行编码和解码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@protocol NSCoding

/**
* 接收对象的实例变量并将其转换为NSCoder类的对象
*
* @param {NSCoder *} encoder
*/

- (void) encodeWithCoder: (NSCoder *) encoder;

/**
* 从NSCoder对象中提取经过转换雪藏的实例变量,并使用它们去初始化新的对象
*
* @param {NSCoder *} decoder
*/

- (id) initWithCoder: (NSCoder *) decoder;

@end

13.1.2 采用协议

说明:要采用某个协议,可以在类的声明中列出该协议的名称,并用<>括起来
语法:可以同时实现多个协议,中间用,分隔

1
2
3
4
5
6
7
@interface 类名: 父类明 <协议1, 协议2, ...
{
// 实例变量
}
// 遵守协议实现的方法
...
@end
1
2
3
4
5
6
7
@interface Car: NSObject <NSCoping, NSCoding>
{
// 实例变量
}
// 遵守协议实现的方法
...
@end

13.1.3 实现协议

13.2 复制

说明:复制分为两类,浅层复制(shallow copy)深层复制(deep copy)

复制 说明
浅层复制 只会复制指向引用对象的指针
深层复制 将复制引用的对象

13.2.1 复制 Engine

说明:没有实例变量或属性。

Engine.h

1
2
3
4
5
6
7
8
#import <Foundation/Foundation.h>

/**
* 采纳 NSCopying 协议
*/

@interface Engine : NSObject <NSCopying>

@end// Engine

Engine.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#import "Engine.h"

@implementation Engine

- (NSString *) description
{
return (@"I am an engine. Vrooom!");
} // description

/**
* 遵守协议,实现复制自身的方法
*
* @param zone 内存区域
*
* @return {id} 复制的对象
*/

- (id) copyWithZone:(NSZone *)zone {
Engine *engineCopy;
// 创建一个当前类(Engine或其子类)创建一个新对象
engineCopy = [[[self class] allocWithZone:zone] init];
return engineCopy;
}// copyWithZone

@end // Engine

13.2.2 复制 Tire

说明:有属性,要考虑实例变量(包括子类的)的复制,需要选择合适的构造函数或者使用setter修改对象的属性。
扩展:可以使用C语言风格的指针运算符直接访问实例变量。

1
2
tireCopy->pressure = presssure;
tireCopy->treadDepth = treadDepth;

技巧:一般来说,当设置属性不太可能涉及额外工作时,我们尽量使用init方法和访问器方法。

Tire.h

1
2
3
4
5
6
7
8
9
10
#import <Foundation/Foundation.h>

@interface Tire : NSObject <NSCopying>

@property float pressure;
@property float treadDepth;

- (id)initWithPressure:(float)pressure treadDepth:(float)treadDepth;

@end

Tire.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#import "Tire.h"

@implementation Tire

/**
* 遵守协议,实现复制自身的方法
*
* @param zone 内存区域
*
* @return 复制的对象
*/

- (id) copyWithZone:(NSZone *)zone {
Tire *tireCopy;
// 调用便利构造器创建对象(属性相同)
tireCopy = [[[self class] allocWithZone:zone] initWithPressure:self.pressure treadDepth:self.treadDepth];
return (tireCopy);
}// copyWithZone
// 构造器
...

@end

13.2.3 复制 Car

Car.h

1
2
3
4
5
6
7
8
#import <Cocoa/Cocoa.h>

@class Tire;
@class Engine;

@interface Car : NSObject <NSCopying>
...
@end // Car

Car.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#import "Car.h"
#import "Engine.h"
#import "Tire.h"

@implementation Car

- (id) copyWithZone:(NSZone *)zone {
Car *carCopy;
// 创建车身
carCopy = [[[self class] allocWithZone:zone] init];
// 基本设置
carCopy.name = self.name;
Engine *engineCopy;
engineCopy = [engine copy];
carCopy.engine = engineCopy;
for (int i= 0; i < 4; i++) {
Tire *tireCopy;
tireCopy = [[self tireAtIndex:i] copy];
[carCopy setTire:tireCopy atIndex:i];
}
return (carCopy);
}// copyWithZone
...

@end // Car

13.2.4 协议和数据

说明:可以在实例变量和方法参数的后面添加<协议名>来对对象是够遵守协议进行类型检查。

1
2
// 要求 object 遵守 NSCopying 协议,否则编译器会给出警告
- (void) setObjectValue: (id<NSCopying>) object;

13.3 Objective-C 2.0 的新特性

说明:Objective-C 2.0中增加了两个新的协议修饰符:@optional@required,用来取代非正式协议

  • @optional:后面的方法声明列表可以实现也可以不实现
  • @required后面的方法列表中的方法必须被实现

优点(相比非正式协议):可以用来在类声明中明确表达我们的意图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@proptocol BaseballPlayer

// 必须实现
- (void) drawHugeSalary;
// 可以实现
@optional
- (void) slideeHome;
- (void) catchBall;
- (void) throwBall;
// 必须实现
@required
- (void) swingBat;

@end//

13.4 委托方法

说明:委托是一个经常与协议共用的特性。
举例:管理者将部分具体工作委托给工作人员完成,工作人员类采纳了指定协议
Alt text

WorkerProtocol.h:工作人员类采纳的协议

1
2
3
4
5
6
7
8
9
10
11
#import <Foundation/Foundation.h>

@protocol WorkerProtocol <NSObject>

@optional
- (void)doSomeOptionalWork;

@required
- (void)doSomeRequiredWork;

@end

Worker1.h + Worker1.m

1
2
3
4
5
6
#import <Foundation/Foundation.h>
#import "WorkerProtocol.h"
// 采纳了协议 WorkerProtocol
@interface Worker1 : NSObject <WorkerProtocol>

@end
1
2
3
4
5
6
7
8
#import "Worker1.h"

@implementation Worker1

- (void)doSomeRequiredWork {
NSLog(@"Worker1 doing required work.");
}
@end

Worker2.h + Worker2.m

1
2
3
4
5
6
#import <Foundation/Foundation.h>
#import "WorkerProtocol.h"
// 采纳了协议 WorkerProtocol
@interface Worker2 : NSObject <WorkerProtocol>

@end
1
2
3
4
5
6
7
8
9
10
11
12
13
#import "Worker2.h"

@implementation Worker2

- (void)doSomeRequiredWork {
NSLog(@"Worker2 doing required work.");
}

- (void)doSomeOptionalWork {
NSLog(@"Worker2 doing optional work.");
}

@end

Manager.h + Manager.m:管理者(会委托部分工作给设置的委托对象)

1
2
3
4
5
6
7
8
#import <Foundation/Foundation.h>
#import "WorkerProtocol.h"

@interface Manager : NSObject
@property (weak) id <WorkerProtocol> delegate;

- (void)doWork;
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#import "Manager.h"

// 定义私有方法
@interface Manager ()
- (void)myWork;
@end

@implementation Manager
@synthesize delegate;

- (void)doWork
{
// 调用委托对象完成必要工作
[delegate doSomeRequiredWork];
// 检查是否有额外工作,有则做
if(YES == [delegate respondsToSelector:@selector(doSomeOptionalWork)])
{
[delegate doSomeOptionalWork];
}
// 执行自己的私有方法
[self myWork];
}

- (void)myWork {
NSLog(@"I am a manager and I am working");
}
@end

main.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#import <Foundation/Foundation.h>
#import "Manager.h"
#import "Worker1.h"
#import "Worker2.h"

int main(int argc, const char * argv[])
{
@autoreleasepool
{
// 管理者
Manager *manager = [[Manager alloc] init];
// 委托对象
Worker1 *worker1 = [[Worker1 alloc] init];
// 为管理者设置委托对象
manager.delegate = worker1;
// 工作(内部委托 worker1 工作)
[manager doWork];

Worker2 *worker2 = [[Worker2 alloc] init];
manager.delegate = worker2;
[manager doWork];
}
return 0;
}

13.5 小结